package PrjDBUtils

import (
	db_utils "dgo/goutils/db-utils"
	"dgo/goutils/utils"
	"errors"
	"fmt"
	"gitee.com/ymofen/supervalue"
	"io"
	"strings"
)

const (
	FIELD_UPFLAG_NAME     string = "____upflag"
	UPFLAG_INSERTORUPDATE int    = 0
	UPFLAG_INSERT         int    = 1
	UPFLAG_UPDATE         int    = 2
)

type DaoUpdateRecord struct {
	Sender      *DAOHelper
	Data        *wrapper.SuperValue
	UpdateTable string
	Uptype      int
	Upcnt       int64
	SQL         string
	Err         error
}

type DAOHelper struct {
	connid       string
	datasourceid string
	repl_db_name string

	Keyfields                string // 用分号分割
	Updatetable              string
	IngoreInsertFields       string // 用分号分割
	IngoreUpdateFields       string // 用分号分割
	FieldPrefix, FieldSuffix string
	QueryPackLowerFieldName  bool
	KeyFieldMap              utils.GSyncMapGroup // jsonkey=fieldname;
	MaxPackageRowCount       int                 // 最大打包数量
	OnRecUpSetValue          func(key *string, val *interface{})
	OnConvertSQLValueFunc    func(key *string, val *interface{}) bool
	OnGetParamFunc           func(key string) (bool, interface{})                                                            // 读取参数值
	AfterUpdateFuncEx        func(uprec *DaoUpdateRecord)                                                                    // 如果前面更新出错则不会继续进行, 优先使用该事件
	AfterUpdateFunc          func(sender *DAOHelper, data *wrapper.SuperValue, uptype int, upcnt int, sql string, err error) // 更新后触发的事件
	TableColumns             []string
	Fields                   string // 需要处理的字段列表, 设置后,只处理这些字段, 以;分割, 末尾需要确保有;应该包含keyfields,

	/**
	  PrepareConnection时 会进行读取, 建立是不会读取的
	*/
	DriverName string
	// 使用ConfigFields进行设置,设置前应该先设定KeyFields
	// 不设置, 不进行过滤
	DBConn *db_utils.DBConn
}

func NewDAOHelper(datasourceid string) *DAOHelper {
	rval := &DAOHelper{connid: datasourceid, datasourceid: datasourceid,
		QueryPackLowerFieldName: Config_LowerFieldName,
		MaxPackageRowCount:      20000}
	return rval
}

func NewDAOHelperEx(datasourceid string, repl_db_name string) *DAOHelper {
	connid := fmt.Sprintf("%s-%s", datasourceid, repl_db_name)
	rval := &DAOHelper{connid: connid, datasourceid: datasourceid, QueryPackLowerFieldName: Config_LowerFieldName, repl_db_name: repl_db_name, MaxPackageRowCount: 3000}
	return rval
}

func (this *DAOHelper) GetParamVal(key string) (bool, interface{}) {
	if this.OnGetParamFunc != nil {
		return this.OnGetParamFunc(key)
	} else {
		return false, nil
	}
}

func (this *DAOHelper) ReloadPojoListMap(sql string, newPojo func() interface{},
	keyfn func(row []db_utils.SqlValue) interface{}, pojoMap *utils.GSyncMapGroup) error {

	checkv := newPojo()
	loadfieldname := utils.GetObjectFieldNameByTag(checkv, "type", "loadflag")
	if len(loadfieldname) > 0 {
		pojoMap.Range(func(key, value interface{}) bool {
			utils.SetObjectVal(value, loadfieldname, -1)
			return true
		})
	}

	_, err := this.RangeRowsEx(sql, func(ridx int, row []db_utils.SqlValue, cols []string, err error) bool {
		key := keyfn(row)
		v := pojoMap.LoadOrStoreFunc(key, newPojo, nil)
		db_utils.AssignObjFromRec(v, row, cols)
		utils.SetObjectVal(v, loadfieldname, 0)
		return true
	})

	if err != nil {
		return err
	}

	if len(loadfieldname) > 0 {
		pojoMap.Range(func(key, value interface{}) bool {
			if utils.GetObjectVal(value, loadfieldname, 0) == -1 {
				pojoMap.Remove(key)
			}
			return true
		})
	}
	return nil
}

/*
*

	jsonkey, fieldname;
*/
func (this *DAOHelper) ConfKeyMap(keyAndFields ...string) *DAOHelper {
	l := len(keyAndFields)
	if l%2 != 0 { // 长度必须是双数
		panic("kv Pairs 长度必须是双数")
	}
	i := 0
	for i < l {
		key := keyAndFields[i]
		val := keyAndFields[i+1]
		this.KeyFieldMap.Set(key, val)
		i += 2
	}
	return this
}

/*
*

	jsonkey=dbkey;jsonkey=dbkey;
*/
func (this *DAOHelper) ConfigKeyMapEx(kvstr string) *DAOHelper {
	this.KeyFieldMap.BatchSetFromStr(kvstr)
	return this
}

func (this *DAOHelper) PrepareDBConn() error {
	if this.DBConn == nil || this.DBConn.IsClosed() {
		db, err := GetDBConnVarDBName(this.datasourceid, this.datasourceid, this.repl_db_name)
		if err != nil {
			return err
		}
		this.DBConn = db
		this.DriverName = db.DriverName()
		this.FieldPrefix, this.FieldSuffix = GetDBConfigFieldPrefixAndSuffix(this.datasourceid)
	}
	return nil
}

/*
*

	onchecktable :
	   返回true继续, 返回false会中断整个恢复
	   sender.Updatetable 设为 空,会忽略该表的回复
	   设置ingorefields可以进行忽略字段更新
	onerr:
	   src:一般为出现异常sql
	   返回false 中断整个恢复
*/
func (this *DAOHelper) RestoreTable(r io.Reader, onchecktable func(sender *DAOHelper, cols []string) bool, onerr func(src string, err error) bool) (int64, error) {
	err := this.PrepareDBConn()
	if err != nil {
		return -1, err
	}

	var cols, table_cols []string
	recUp := db_utils.NewRecUpdateUpbuilder()
	all_n := int64(0)
	j := int64(0)
	var lasterr error = nil

	utils.ReadPerLine(r, func(idx int64, line []byte) bool {
		//line = bytes.TrimRight(line, "\r")
		//golog.Debugf("%d:[%d][%s]", j, len(line), string(line));
		if len(line) == 0 {
			if j == 0 {
				return true
			} else {
				if j > 2 {
					all_n += j - 2
				}
				j = 0
				return true // 完成一个
			}
		}
		if j == 0 {
			str := string(line)
			strs := strings.SplitN(str, ",", 2)
			this.Updatetable = strs[0]
			if len(strs) > 1 {
				this.Keyfields = strs[1]
			}
			j++
		} else if j == 1 {
			str := string(line)
			cols = strings.Split(str, ",")
			this.ResetForUpdate()
			table_cols, _ = this.DBConn.GetColumnsByTable(this.Updatetable) // 获取表的所有字段
			for i := 0; i < len(table_cols); i++ {
				if utils.StrIndex(strings.ToLower(table_cols[i]), cols...) == -1 {
					cols[i] = "" // 置空不进行更新
				}
			}
			if onchecktable != nil {
				if !onchecktable(this, cols) {
					return false
				}
			}
			j++
		} else {
			if len(this.Updatetable) == 0 {
				return true // 忽略更新
			}
			json, err := wrapper.NewSVFromBuf(line)
			if err != nil {
				return onerr(string(line), err)
			}
			recUp.Clear()
			for i := 0; i < len(cols); i++ {
				field := cols[i]
				if len(field) > 0 {
					recUp.SetValue(field, json.VarByIdx(i))
				}
			}
			if len(this.Keyfields) == 0 {
				lvsql := recUp.BuildInsert(this.Updatetable)
				_, err := this.DBConn.ExecSql(lvsql)
				if err != nil {
					lasterr = err
					if onerr != nil {
						return onerr(lvsql, err)
					}
				} else {
					j++
				}
			} else {
				_, errsql, err := this.DBConn.ExecuteUpOrInsertByDB(recUp, this.Keyfields, this.Updatetable)
				if err != nil {
					lasterr = err
					if onerr != nil {
						return onerr(errsql, err)
					}
				} else {
					j++
				}
			}
		}
		return true
	})
	if j > 2 {
		all_n += j - 2
	}
	return all_n, lasterr
}

func (this *DAOHelper) BackupTable(w io.Writer, tablename, keyfields string) error {
	err := this.PrepareDBConn()
	if err != nil {
		return err
	}

	var backup db_utils.DataBackup
	backup.ConfigWriter(w)
	this.DBConn.RangeRowsEx(func(ridx int, row []db_utils.SqlValue, cols []string, err error) bool {
		if ridx == 0 {
			backup.WriteTable(tablename, keyfields, cols)
		}
		backup.WriteRow(row)
		return true
	}, fmt.Sprintf("select * from %s", tablename))
	w.Write(db_utils.BUF_Enter)

	return nil
}

func (this *DAOHelper) Close() error {
	dbconn := this.DBConn
	this.DBConn = nil
	if dbconn != nil {
		dbconn.AutoDoneTrans()
		return dbconn.Close()
	}
	return nil
}

func (this *DAOHelper) innerCheckPrepare() error {
	if this.DBConn != nil {
		return nil
	}
	return this.PrepareDBConn()
}

func (this *DAOHelper) innerAction(actionFunc func(db *db_utils.DBConn) (int64, string, error)) (int64, string, error) {
	var db *db_utils.DBConn
	err := this.PrepareDBConn()
	if err != nil {
		return -1, "", err
	} else {
		db = this.DBConn
	}
	//if this.DBConn == nil {
	//	db1, err := GetDBConnVarDBName(this.connid, this.datasourceid, this.repl_db_name)
	//	if err != nil {
	//		return -1, "", err
	//	}
	//	defer db1.Close()
	//	db = db1
	//} else {
	//	db = this.DBConn
	//}

	return actionFunc(db)
}

func (this *DAOHelper) innerRecActionEx(req *wrapper.SuperValue, updateTable string,
	ignorekeys string, // 忽略req中的key 用分号分割
	afterGetDBFunc func(db *db_utils.DBConn) bool, // 得到DBconn后执行的事件
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool, // 执行更新之前的事件
	actionFunc func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error), // 执行动作
) (int64, string, error) {
	var db *db_utils.DBConn
	err := this.PrepareDBConn()
	if err != nil {
		return -1, "", err
	}
	db = this.DBConn
	//if this.DBConn == nil {
	//	db1, err := GetDBConnVarDBName(this.datasourceid, this.repl_db_name)
	//	if err != nil {
	//		return -1, "", err
	//	}
	//	defer db1.Close()
	//	db = db1
	//} else {
	//	db = this.DBConn
	//}

	if afterGetDBFunc != nil {
		if !afterGetDBFunc(db) {
			return 0, "", nil
		}
	}

	if req != nil {
		cols := this.TableColumns
		if (cols == nil) || (len(cols) > 0) {
			cols, _ = db.GetColumnsByTable(updateTable) // 获取表的所有字段
			for k, v := range cols {
				cols[k] = strings.ToLower(v)
			}
			this.TableColumns = cols
		}

		if len(ignorekeys) > 0 && !strings.HasSuffix(ignorekeys, ";") {
			ignorekeys += ";"
		}
		recUp := db_utils.NewRecUpdateUpbuilder()
		recUp.DbType = db.DbType()
		recUp.FieldSuffix = this.FieldSuffix
		recUp.FieldPrefix = this.FieldPrefix
		if this.OnRecUpSetValue != nil {
			recUp.OnSetValue = this.OnRecUpSetValue
		}
		if this.OnConvertSQLValueFunc != nil {
			recUp.OnConvertSQLValueFunc = this.OnConvertSQLValueFunc
		}
		for i := 0; i < req.Count(); i++ {
			key := req.KeyNameByIndex(i)
			if len(ignorekeys) > 1 && strings.Contains(ignorekeys, key+";") {
				continue
			}
			field := this.KeyFieldMap.Get(key, key).(string)
			if len(cols) > 0 && utils.StrIndexIgnoreCase(field, cols...) == -1 {
				continue
			}
			if len(this.Fields) > 1 && !strings.Contains(this.Fields, field+";") {
				continue
			}
			CheckSetUpdateVal2(recUp, req, field, key)
		}

		if beforeUpCb != nil {
			if !beforeUpCb(db, recUp) {
				return 0, "", nil
			}
		}
		return actionFunc(db, recUp)
	} else {
		return actionFunc(db, nil)
	}
}

func (this *DAOHelper) ResetForUpdate() {

}

func (this *DAOHelper) ConfigFields(fields string) {
	if len(fields) > 0 && !strings.HasSuffix(fields, ";") {
		fields += ";"
	}
	if len(fields) > 0 {
		fields += this.Keyfields
		if !strings.HasSuffix(fields, ";") {
			fields += ";"
		}
	}
	this.Fields = fields
}

/*
**

	ignorekeys用分号分割
*/
func (this *DAOHelper) InsertEx2(req *wrapper.SuperValue, updateTable, ignorekeys string,
	afterGetDBFunc func(db *db_utils.DBConn) bool,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(updateTable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	if req == nil || req.Count() == 0 {
		return 0, "", fmt.Errorf("请指定更新的数据")
	}
	var UpdateRec DaoUpdateRecord
	UpdateRec.UpdateTable = updateTable
	UpdateRec.Sender = this
	n, rsql, err := this.innerRecActionEx(req, updateTable, ignorekeys, afterGetDBFunc, beforeUpCb,
		func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
			recUp.SetIgnoreInsertFields(ignorekeys)
			val := req.ValueByName("__insert")
			if val != nil {
				val.Range(func(key string, val *wrapper.SuperValue) bool {
					recUp.SetValue(key, val.Variant())
					return true
				})
			}

			lvsql := recUp.BuildInsert(updateTable)
			n, err := db.ExecSql(lvsql)
			if this.AfterUpdateFunc != nil {
				this.AfterUpdateFunc(this, req, db_utils.ACTION_APPEND, int(n), lvsql, err)
			}
			if err == nil && this.AfterUpdateFuncEx != nil {
				UpdateRec.SQL = lvsql
				UpdateRec.Uptype = db_utils.ACTION_APPEND
				UpdateRec.Data = req
				UpdateRec.Err = err
				UpdateRec.Upcnt = n
				this.AfterUpdateFuncEx(&UpdateRec)
				return n, UpdateRec.SQL, UpdateRec.Err
			}
			return n, lvsql, err

		})
	return n, rsql, err
}

/*
**

	ignorekeys用分号分割
*/
func (this *DAOHelper) InsertEx(req *wrapper.SuperValue, ignorekeys string,
	afterGetDBFunc func(db *db_utils.DBConn) bool,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	return this.InsertEx2(req, this.Updatetable, ignorekeys, afterGetDBFunc, beforeUpCb)
}

/*
**

	  ignorekeys 用分号分割, 忽略data.list中的key

	  data:
		{ "__update":{"fuserid":""}, "__insert":{"fuserid":""}, "list":[{"__uptype":1},{}] }
	    "__update":{}  更新时进行赋值
	    "__insert":{}  插入时进行赋值

	  uptype:  0:根据rec中的__uptype 确定
	    1:修改, 2:新增, 其他值:不存在则插入,
*/
func (this *DAOHelper) BatchUpdateEx(data *wrapper.SuperValue, ignorekeys string, uptype int,
	onBeforeUpFunc func(rec *db_utils.RecUpdateUpbuilder, action int, data *wrapper.SuperValue) bool) (cnt int64, rsql string, rerr error) {

	return this.BatchUpdate(data, this.Updatetable, this.Keyfields, ignorekeys, ignorekeys, uptype, onBeforeUpFunc)

	if len(this.Updatetable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}

	lst := data.ValueByName("list")
	if lst == nil {
		return 0, "", nil
	}

	cnt = 0
	var UpdateRec DaoUpdateRecord
	UpdateRec.Sender = this
	UpdateRec.UpdateTable = this.Updatetable
	lst.Range(func(key string, val *wrapper.SuperValue) bool {
		n, sql1, err := this.innerRecActionEx(val, this.Updatetable, ignorekeys, nil, nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
			recuptype := uptype
			if recuptype == 0 {
				recuptype = val.IntByName("__uptype", 0)
			}

			recUp.BeforeBuildFunc = func(sender *db_utils.RecUpdateUpbuilder, action int) bool {
				if onBeforeUpFunc != nil {
					return onBeforeUpFunc(recUp, action, val)
				}
				return true
			}
			if recuptype == db_utils.ACTION_APPEND {
				lvsql := recUp.BuildInsert(this.Updatetable)
				if len(lvsql) > 0 {
					n, err := db.ExecSql(lvsql)
					if this.AfterUpdateFunc != nil {
						this.AfterUpdateFunc(this, val, db_utils.ACTION_APPEND, int(n), lvsql, err)
					}
					if err == nil && this.AfterUpdateFuncEx != nil {
						UpdateRec.SQL = lvsql
						UpdateRec.Uptype = db_utils.ACTION_APPEND
						UpdateRec.Data = val
						UpdateRec.Err = err
						UpdateRec.Upcnt = n
						this.AfterUpdateFuncEx(&UpdateRec)
						return n, UpdateRec.SQL, UpdateRec.Err
					}
					return n, lvsql, err
				}
				return 0, lvsql, nil
			} else if recuptype == db_utils.ACTION_UPDATE {
				lvsql := recUp.BuildUpdate(this.Updatetable, this.Keyfields)
				if len(lvsql) > 0 {
					n, err := db.ExecSql(lvsql)
					if this.AfterUpdateFunc != nil {
						this.AfterUpdateFunc(this, val, db_utils.ACTION_UPDATE, int(n), lvsql, err)
					}
					if err == nil && this.AfterUpdateFuncEx != nil {
						UpdateRec.SQL = lvsql
						UpdateRec.Uptype = db_utils.ACTION_UPDATE
						UpdateRec.Data = val
						UpdateRec.Err = err
						UpdateRec.Upcnt = n
						this.AfterUpdateFuncEx(&UpdateRec)
						return n, UpdateRec.SQL, UpdateRec.Err
					}
					return n, lvsql, err
				}
				return 0, lvsql, nil
			} else {
				action, n, lvsql, err := db.ExecuteUpOrInsertByDBEx(recUp, this.Keyfields, this.Updatetable)
				if this.AfterUpdateFunc != nil {
					this.AfterUpdateFunc(this, val, action, int(n), lvsql, err)
				}
				if err == nil && this.AfterUpdateFuncEx != nil {
					UpdateRec.SQL = lvsql
					UpdateRec.Uptype = action
					UpdateRec.Data = val
					UpdateRec.Err = err
					UpdateRec.Upcnt = n
					this.AfterUpdateFuncEx(&UpdateRec)
					return n, UpdateRec.SQL, UpdateRec.Err
				}
				return n, lvsql, err
			}
		})
		cnt += n
		rsql = sql1
		if err != nil {
			rerr = err
			return false
		}
		return true
	})

	return
}

/*
**

	  ignorekeys 用分号分割, 忽略data.list中的key

	  data:
		{ "__update":{"fuserid":""}, "__insert":{"fuserid":""}, "list":[{"__uptype":1},{}] }
	    "__update":{}  更新时进行赋值
	    "__insert":{}  插入时进行赋值

	  uptype:  0:根据rec中的__uptype 确定
	    1:修改, 2:新增, 其他值:不存在则插入,
*/
func (this *DAOHelper) BatchUpdate(data *wrapper.SuperValue, updateTable string, keyField string, ignoreUpdateFields, ignoreInsertFields string, uptype int,
	onBeforeUpFunc func(rec *db_utils.RecUpdateUpbuilder, action int, data *wrapper.SuperValue) bool) (cnt int64, rsql string, rerr error) {
	if len(updateTable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	var lst *wrapper.SuperValue
	if data.IsArray() {
		lst = data
	} else {
		lst = data.ValueByName("list")
	}

	if lst == nil {
		return 0, "", nil
	}

	var uprec DaoUpdateRecord
	uprec.Sender = this
	uprec.UpdateTable = updateTable

	cnt = 0
	lst.Range(func(key string, val *wrapper.SuperValue) bool {
		n, sql1, err := this.innerRecActionEx(val, updateTable, "", nil, nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {

			recuptype := uptype
			if recuptype == 0 {
				recuptype = val.IntByName("__uptype", 0)
			}

			recUp.BeforeBuildFunc = func(sender *db_utils.RecUpdateUpbuilder, action int) bool {
				if action == db_utils.ACTION_APPEND {
					recUp.SetIgnoreInsertFields(ignoreInsertFields)
					val := data.ValueByName("__insert")
					if val != nil {
						val.Range(func(key string, val *wrapper.SuperValue) bool {
							recUp.SetValue(key, val.Variant())
							return true
						})
					}
				} else if action == db_utils.ACTION_UPDATE {
					recUp.SetIgnoreUpdateFields(ignoreUpdateFields)
					val := data.ValueByName("__update")
					if val != nil {
						val.Range(func(key string, val *wrapper.SuperValue) bool {
							recUp.SetValue(key, val.Variant())
							return true
						})
					}
				}

				if onBeforeUpFunc != nil {
					return onBeforeUpFunc(recUp, action, val)
				}
				return true
			}
			if recuptype == db_utils.ACTION_APPEND {
				lvsql := recUp.BuildInsert(updateTable)
				n, err := db.ExecSql(lvsql)
				if this.AfterUpdateFunc != nil {
					this.AfterUpdateFunc(this, val, recuptype, int(n), lvsql, err)
				}
				if err == nil && this.AfterUpdateFuncEx != nil {
					uprec.SQL = lvsql
					uprec.Uptype = db_utils.ACTION_APPEND
					uprec.Data = val
					uprec.Err = err
					uprec.Upcnt = n
					this.AfterUpdateFuncEx(&uprec)
					return n, uprec.SQL, uprec.Err
				}
				return n, lvsql, err
			} else if recuptype == db_utils.ACTION_UPDATE {
				lvsql := recUp.BuildUpdate(updateTable, keyField)
				n, err := db.ExecSql(lvsql)
				if this.AfterUpdateFunc != nil {
					this.AfterUpdateFunc(this, val, recuptype, int(n), lvsql, err)
				}
				if err == nil && this.AfterUpdateFuncEx != nil {
					uprec.SQL = lvsql
					uprec.Uptype = db_utils.ACTION_APPEND
					uprec.Data = val
					uprec.Err = err
					uprec.Upcnt = n
					this.AfterUpdateFuncEx(&uprec)
					return n, uprec.SQL, uprec.Err
				}
				return n, lvsql, err
			} else {
				action, n, lvsql, err := db.ExecuteUpOrInsertByDBEx(recUp, keyField, updateTable)
				if this.AfterUpdateFunc != nil {
					this.AfterUpdateFunc(this, val, action, int(n), lvsql, err)
				}
				if err == nil && this.AfterUpdateFuncEx != nil {
					uprec.SQL = lvsql
					uprec.Uptype = action
					uprec.Data = val
					uprec.Err = err
					uprec.Upcnt = n
					this.AfterUpdateFuncEx(&uprec)
					return n, uprec.SQL, uprec.Err
				}
				return n, lvsql, err
			}

		})
		cnt += n
		rsql = sql1
		if err != nil {
			rerr = err
			return false
		}
		return true
	})

	return
}

func (this *DAOHelper) GetFirstValAsString(sql string, def string, args ...interface{}) (string, error) {
	err := this.PrepareDBConn()
	if err != nil {
		return def, err
	}
	rows, err := this.DBConn.Query(sql, args...)
	if err != nil {
		return def, err
	}

	defer rows.Close()

	if rows.Next() {
		rowValues, err := db_utils.GetRowValues(rows)
		if err != nil {
			return def, err
		} else {
			return rowValues[0].AsString(def), nil
		}
	}
	return def, nil
}

func (this *DAOHelper) GetFirstValAsInt64(sql string, def int64, args ...interface{}) (int64, error) {
	err := this.PrepareDBConn()
	if err != nil {
		return def, err
	}
	rows, err := this.DBConn.Query(sql, args...)
	if err != nil {
		return def, err
	}

	defer rows.Close()

	if rows.Next() {
		rowValues, err := db_utils.GetRowValues(rows)
		if err != nil {
			return def, err
		} else {
			return rowValues[0].AsInt64(def), nil
		}
	}
	return def, nil
}

func (this *DAOHelper) GetFirstRow(sql string, args ...interface{}) ([]db_utils.SqlValue, error) {
	err := this.PrepareDBConn()
	if err != nil {
		return nil, err
	}
	return this.DBConn.GetFirstRow(sql, args...)
}

func (this *DAOHelper) UpdateEx2(req *wrapper.SuperValue, updateTable, keyfields string, ignorekeys string,
	afterGetDBFunc func(db *db_utils.DBConn) bool,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(updateTable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	var uprec DaoUpdateRecord
	uprec.Sender = this
	uprec.UpdateTable = updateTable
	n, rsql, err := this.innerRecActionEx(req, updateTable, ignorekeys, afterGetDBFunc, beforeUpCb, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
		val := req.ValueByName("__update")
		if val != nil {
			val.Range(func(key string, val *wrapper.SuperValue) bool {
				recUp.SetValue(key, val.Variant())
				return true
			})
		}

		lvsql := recUp.BuildUpdate(updateTable, keyfields)
		n, err := db.ExecSql(lvsql)
		if this.AfterUpdateFunc != nil {
			this.AfterUpdateFunc(this, req, db_utils.ACTION_UPDATE, int(n), lvsql, err)
		}
		if err == nil && this.AfterUpdateFuncEx != nil {
			uprec.SQL = lvsql
			uprec.Uptype = db_utils.ACTION_UPDATE
			uprec.Data = req
			uprec.Err = err
			uprec.Upcnt = n
			this.AfterUpdateFuncEx(&uprec)
			return n, uprec.SQL, uprec.Err
		}
		return n, lvsql, err
	})
	return n, rsql, err
}

/*
**

	ignorekeys用分号分割
*/
func (this *DAOHelper) UpdateEx(req *wrapper.SuperValue, ignorekeys string,
	afterGetDBFunc func(db *db_utils.DBConn) bool,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(this.Updatetable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	return this.UpdateEx2(req, this.Updatetable, this.Keyfields, ignorekeys, afterGetDBFunc, beforeUpCb)
}

func (this *DAOHelper) InsertOrUpdateEx2(req *wrapper.SuperValue, updateTable, keyfields, ignoreUpdateFields, ignoreInsertFields string,
	beforeUpCb func(db *db_utils.DBConn, action int, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(updateTable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	var uprec DaoUpdateRecord
	uprec.Sender = this
	uprec.UpdateTable = updateTable
	n, rsql, err := this.innerRecActionEx(req, updateTable, "", nil, nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
		recUp.KeyFields = keyfields
		recUp.Uptablename = updateTable

		if len(ignoreUpdateFields) > 0 {
			recUp.SetIgnoreUpdateFields(ignoreUpdateFields)
		}

		recUp.SetIgnoreInsertFields(ignoreInsertFields)
		recUp.BeforeBuildFunc = func(sender *db_utils.RecUpdateUpbuilder, action int) bool {
			if beforeUpCb != nil {
				return beforeUpCb(db, action, recUp)
			}
			return true
		}
		action, n, lvsql, err := db.ExecuteUpOrInsertByDBEx(recUp, keyfields, updateTable)
		if this.AfterUpdateFunc != nil {
			this.AfterUpdateFunc(this, req, action, int(n), lvsql, err)
		}
		if err == nil && this.AfterUpdateFuncEx != nil {
			uprec.SQL = lvsql
			uprec.Uptype = action
			uprec.Data = req
			uprec.Err = err
			uprec.Upcnt = n
			this.AfterUpdateFuncEx(&uprec)
			return n, uprec.SQL, uprec.Err
		}
		return n, lvsql, err
		//return n, lvsql, err
	})
	return n, rsql, err
}

/*
**

	ignorekeys, ignoreUpdateFields用分号分割
*/
func (this *DAOHelper) InsertOrUpdateEx(req *wrapper.SuperValue,
	beforeUpCb func(db *db_utils.DBConn, action int, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(this.Updatetable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	return this.InsertOrUpdateEx2(req, this.Updatetable, this.Keyfields, this.IngoreUpdateFields, this.IngoreInsertFields, beforeUpCb)
}

/*
**

	ignorekeys, ignoreUpdateFields用分号分割
*/
func (this *DAOHelper) InsertOrUpdate(req *wrapper.SuperValue, ignoreUpdateFields, ignorekeys string,
	afterGetDBFunc func(db *db_utils.DBConn) bool,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	if len(this.Updatetable) == 0 {
		return 0, "", fmt.Errorf("请指定更新表名")
	}
	var uprec DaoUpdateRecord
	uprec.Sender = this
	uprec.UpdateTable = this.Updatetable
	n, rsql, err := this.innerRecActionEx(req, this.Updatetable, ignorekeys, afterGetDBFunc, beforeUpCb, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
		recUp.KeyFields = this.Keyfields
		recUp.Uptablename = this.Updatetable

		if len(ignoreUpdateFields) > 0 {
			recUp.SetIgnoreUpdateFields(ignoreUpdateFields)
		} else if len(this.IngoreUpdateFields) > 0 {
			recUp.SetIgnoreUpdateFields(this.IngoreUpdateFields)
		}

		recUp.SetIgnoreInsertFields(this.IngoreInsertFields)
		recUp.BeforeBuildFunc = func(sender *db_utils.RecUpdateUpbuilder, action int) bool {
			if action == db_utils.ACTION_APPEND {
				val := req.ValueByName("__insert")
				if val != nil {
					val.Range(func(key string, val *wrapper.SuperValue) bool {
						recUp.SetValue(key, val.Variant())
						return true
					})
				}

			} else if action == db_utils.ACTION_UPDATE {
				val := req.ValueByName("__update")
				if val != nil {
					val.Range(func(key string, val *wrapper.SuperValue) bool {
						recUp.SetValue(key, val.Variant())
						return true
					})
				}
			}
			return true
		}
		action, n, lvsql, err := db.ExecuteUpOrInsertByDBEx(recUp, this.Keyfields, this.Updatetable)
		if this.AfterUpdateFunc != nil {
			this.AfterUpdateFunc(this, req, action, int(n), lvsql, err)
		}
		if err == nil && this.AfterUpdateFuncEx != nil {
			uprec.SQL = lvsql
			uprec.Uptype = action
			uprec.Data = req
			uprec.Err = err
			uprec.Upcnt = n
			this.AfterUpdateFuncEx(&uprec)
			return n, uprec.SQL, uprec.Err
		}
		return n, lvsql, err
	})
	return n, rsql, err
}

/*
**

	ignorekeys用分号分割
*/
func (this *DAOHelper) Insert(req *wrapper.SuperValue, ignorekeys string,
	beforeUpCb func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool) (int64, string, error) {
	n, rsql, err := this.InsertEx(req, ignorekeys, nil, beforeUpCb)
	return n, rsql, err
}

func (this *DAOHelper) RecordCount(sql string) (int64, error) {
	err := this.innerCheckPrepare()
	if err != nil {
		return -1, err
	}
	n, err := this.DBConn.GetRowCount(sql)
	return int64(n), err
}

func (this *DAOHelper) AssertNoRecord(db *db_utils.DBConn, errmsg string, sql string) error {
	n, err := db.GetRowCount(sql)
	if n == 0 {
		return nil
	} else if err != nil {
		return err
	} else {
		return errors.New(errmsg)
	}
}

/*
*

	{
	    "table":"bas_vehicle",
	    "trans":0,
	    "ignoreerr":0,
	    "keyfields":"ftermid",
	    "fields":"flat;flng;",
	    "ignoreupfields":"fcreatetime;fid",
	    "upfields":"flng;flat",
	    "list":[
	        {"____upflag":0, "fid":1002, "ftermid":"17098900736", "flat":34.1},
	        {"____upflag":0, "fid":1005, "ftermid":"17098900737", "flat":34.2}
	     ]
	}

____upflag : 0:不存在就插入
table: 表名
fields: 需要处理的(包含插入)的数据字段
ignoreupfields: 忽略更新的字段(插入时忽略该选项)
trans: 事务标记 1:开启事务(如果有错误则回滚) 忽略ignoreerr标志
ignoreerr: 1: 忽略异常更新记录. 忽略时会执行onerr回调函数
*/
func (this *DAOHelper) UpdateBatch(json *wrapper.SuperValue, onerr func(errsql string, err error)) (n int64, errsql string, err error) {
	n = 0
	err = this.PrepareDBConn()
	if err != nil {
		return
	}
	updatetable := json.StringByName("table", this.Updatetable)
	keyfields := json.StringByName("keyfields", this.Keyfields)
	this.ConfigFields(json.StringByName("fields", this.Fields))
	ignoreupfields := json.StringByName("ignoreupfields", this.IngoreUpdateFields)
	ignoreerr := json.IntByName("ignoreerr", 0)
	lst := json.Find("list")
	if lst == nil {
		return
	}
	transflag := json.IntByName("trans", 0)
	if transflag == 1 {
		this.DBConn.BeginTrans()
		defer func() {
			if err != nil {
				this.DBConn.RollBack()
			} else {
				this.DBConn.Commit()
			}
		}()
	}

	lst.Range(func(key string, val *wrapper.SuperValue) bool {
		__upflag := val.IntByName(FIELD_UPFLAG_NAME, 0)
		val.RemoveKey(FIELD_UPFLAG_NAME)
		upn, lverrsql, e := this.innerRecActionEx(val, updatetable, "", nil, nil,
			func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
				if __upflag == UPFLAG_INSERT {
					lvsql := recUp.BuildInsert(updatetable)
					n, err := db.ExecSql(lvsql)
					return n, lvsql, err
				} else if __upflag == UPFLAG_UPDATE {
					lvsql := recUp.BuildUpdate(updatetable, keyfields)
					n, err := db.ExecSql(lvsql)
					return n, lvsql, err
				} else {
					recUp.KeyFields = keyfields
					recUp.Uptablename = updatetable
					recUp.SetIgnoreUpdateFields(ignoreupfields)
					return db.ExecuteUpOrInsertByRecUp(recUp)
				}
			})
		if e != nil {
			if transflag == 1 {
				n = 0
				err = e
				errsql = lverrsql
				return false
			}
			if ignoreerr == 0 { // 不忽略错误, 退出
				err = e
				errsql = lverrsql
				return false
			} else {
				if onerr != nil {
					onerr(lverrsql, e)
				}
			}
		} else {
			n += upn
		}
		return true

	})

	return
}

func (this *DAOHelper) DeleteEx2(req *wrapper.SuperValue, updateTable, keyfields string,
	beforeUpCb func(recUp *db_utils.RecUpdateUpbuilder)) (int64, string, error) {
	n, errsql, err := this.innerRecActionEx(req, "", "", nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool {
		if beforeUpCb != nil {
			beforeUpCb(recUp)
		}
		return true
	}, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
		lvsql := recUp.BuildDelete(updateTable, keyfields)
		if len(lvsql) == 0 {
			return 0, "", fmt.Errorf("没有指定删除条件")
		}
		n, err := db.ExecSql(lvsql)
		if this.AfterUpdateFunc != nil {
			this.AfterUpdateFunc(this, req, db_utils.ACTION_DELETE, int(n), lvsql, err)
		}
		return n, lvsql, err
	})
	return n, errsql, err
}

func (this *DAOHelper) Delete(req *wrapper.SuperValue, keyfields string,
	beforeUpCb func(recUp *db_utils.RecUpdateUpbuilder)) (int64, string, error) {
	n, errsql, err := this.innerRecActionEx(req, "", "", nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) bool {
		if beforeUpCb != nil {
			beforeUpCb(recUp)
		}
		return true
	}, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
		lvsql := recUp.BuildDelete(this.Updatetable, keyfields)
		if len(lvsql) == 0 {
			return 0, "", fmt.Errorf("没有指定删除条件")
		}
		n, err := db.ExecSql(lvsql)
		if this.AfterUpdateFunc != nil {
			this.AfterUpdateFunc(this, req, db_utils.ACTION_DELETE, int(n), lvsql, err)
		}
		return n, lvsql, err
	})
	return n, errsql, err
}

/*
**

	n 执行了RangeFunc多少次
*/
func (this *DAOHelper) RangeRows(lvsql string, range_func func(row []db_utils.SqlValue, err error) bool) (int, error) {
	err := this.innerCheckPrepare()
	if err != nil {
		return -1, err
	}
	n, err := this.DBConn.RangeRows(range_func, lvsql)
	return int(n), err
}

/*
**

	n 执行了RangeFunc多少次
	多了对应的colname
*/
func (this *DAOHelper) RangeRowsEx(lvsql string, range_func func(ridx int, row []db_utils.SqlValue, cols []string, err error) bool) (int, error) {
	err := this.innerCheckPrepare()
	if err != nil {
		return -1, err
	}
	n, err := this.DBConn.RangeRowsEx(range_func, lvsql)
	return int(n), err
	//n, _, err := this.innerRecActionEx(nil, "", nil, nil, func(db *db_utils.DBConn, recUp *db_utils.RecUpdateUpbuilder) (int64, string, error) {
	//	n, err := db.RangeRowsEx(range_func, lvsql)
	//	return int64(n), lvsql, err
	//})
	//return int(n), err
}

func (this *DAOHelper) QueryCustomEx(countsqlfun func() string,
	selfun func() string,
	recPreProcess TPreProcessRecCB,
	onsqlfunc func(sql *string, action int),
	resultLst func() *wrapper.SuperValue,
	oncountfun func(cnt int),
) (cnt int, lastsql string, err error) {

	err = this.PrepareDBConn()
	if err != nil {
		return
	}

	if countsqlfun != nil {
		lvsql := countsqlfun()
		if len(lvsql) > 0 {
			if onsqlfunc != nil {
				onsqlfunc(&lvsql, 1)
			}
			v, e := this.DBConn.GetFirstVal(lvsql)
			if e != nil {
				err = e
				lastsql = lvsql
				return
			}

			if v == nil {
				oncountfun(0)
			} else {
				oncountfun(v.AsInt(0))
			}
		}
	}

	lvsql := selfun()
	if onsqlfunc != nil {
		onsqlfunc(&lvsql, 0)
	}
	lastsql = lvsql
	cnt, err = this.ListEx(lastsql, resultLst, recPreProcess)

	return
}

/*
*

	recfield:"fid"
*/
func (this *DAOHelper) QueryCustom(countsqlfun func() string,
	selfun func(sb *db_utils.SQLBuilder),
	whereFunc func(wb *db_utils.SQLBuilder),
	orderByFunc func(sb *db_utils.SQLBuilder),
	pagefunc func() (posi int, num int),
	recPreProcess TPreProcessRecCB,
	onsqlfunc func(sql *string),
	resultLst func() *wrapper.SuperValue,
	oncountfun func(cnt int),
) (cnt int, lastsql string, err error) {

	err = this.PrepareDBConn()
	if err != nil {
		return
	}

	var sqlB, whereB db_utils.SQLBuilder

	whereFunc(&whereB)

	if countsqlfun != nil {
		sqlB.AddAny(countsqlfun())
		if sqlB.Len() > 0 {
			if whereB.Len() > 0 {
				if sqlB.Whereflag == 0 {
					if whereB.Whereflag == 0 {
						sqlB.Where()
					}
				} else {
					sqlB.And()
				}
				sqlB.Add(&whereB)
			}
			lvsql := sqlB.String()
			if onsqlfunc != nil {
				onsqlfunc(&lvsql)
			}
			v, e := this.DBConn.GetFirstVal(lvsql)
			if e != nil {
				err = e
				lastsql = lvsql
				return
			}

			if v == nil {
				oncountfun(0)
			} else {
				oncountfun(v.AsInt(0))
			}
			sqlB.Reset("")
		}
	}

	posi, num := pagefunc()
	selfun(&sqlB)
	if whereB.Len() > 0 {
		if sqlB.Whereflag == 0 {
			if whereB.Whereflag == 0 {
				sqlB.Where()
			}
		} else {
			sqlB.And()
		}
		sqlB.Add(&whereB)
	}
	if orderByFunc != nil {
		orderByFunc(&sqlB)
	}
	if posi >= 0 && num > 0 {
		sqlB.Limit(posi, num)
	}

	lastsql = sqlB.String()
	if onsqlfunc != nil {
		onsqlfunc(&lastsql)
	}
	cnt, err = this.ListEx(lastsql, resultLst, recPreProcess)

	return
}

/*
*
dao.QueryData("select * from usr_projects", json, nil)

	json 如果是数组,返回所有数据列表, 如果是对象,则只填充第一条
*/
func (this *DAOHelper) QueryData(sql string, data *wrapper.SuperValue, recPreProcess TPreProcessRecCB) (cnt int, err error) {
	cnt, _, err = this.QueryCustomEx(nil, func() string {
		return sql
	}, recPreProcess, nil, func() *wrapper.SuperValue {
		return data
	}, nil)
	return
}

/*
*

	recfield:"fid"
*/
func (this *DAOHelper) QueryEx(countsqlfun func() string,
	selfun func(sb *db_utils.SQLBuilder),
	whereFunc func(wb *db_utils.SQLBuilder),
	pagefunc func() (posi int, num int),
	recPreProcess TPreProcessRecCB,
	resultLst func() *wrapper.SuperValue,
	oncountfun func(cnt int),
) (cnt int, lastsql string, err error) {
	return this.QueryCustom(countsqlfun, selfun, whereFunc, nil, pagefunc, recPreProcess, nil, resultLst, oncountfun)
}

/*
*

	recfield:"fid"
*/
func (this *DAOHelper) Query(json, resp *wrapper.SuperValue,
	countsqlfun func() string,
	selfun func(sb *db_utils.SQLBuilder),
	searchWBFunc func(wb *db_utils.SQLBuilder, searchval string),
	fixedWbFunc func(wb *db_utils.SQLBuilder),
	recPreProcess TPreProcessRecCB,
) (cnt int, lastsql string, err error) {
	return this.QueryOrderby(json, resp, countsqlfun, selfun, searchWBFunc, fixedWbFunc, nil, recPreProcess)
}

/*
*

	recfield:"fid"
*/
func (this *DAOHelper) QueryOrderby(json, resp *wrapper.SuperValue,
	countsqlfun func() string,
	selfun func(sb *db_utils.SQLBuilder),
	searchWBFunc func(wb *db_utils.SQLBuilder, searchval string),
	fixedWbFunc func(wb *db_utils.SQLBuilder),
	orderbyFunc func(sb *db_utils.SQLBuilder),
	recPreProcess TPreProcessRecCB,
) (cnt int, lastsql string, err error) {

	err = this.PrepareDBConn()
	if err != nil {
		return
	}

	var sqlB, whereB db_utils.SQLBuilder

	recid := json.VarByName("recid")
	reqCnt := json.IntByName("reqcnt", 0)
	if recid != nil {
		reqCnt = 0
		whereB.EqualValues(json.StringByName("recfield", ""), recid)
	} else {
		str := json.StringByName("searchval", "")
		if len(str) > 0 {
			searchWBFunc(&whereB, str)
		}
	}
	if fixedWbFunc != nil {
		fixedWbFunc(&whereB)
	}

	if reqCnt == 1 {
		if countsqlfun == nil {
			resp.SetInt("cnt", 0)
		} else {
			sqlB.AddAny(countsqlfun())
			if whereB.Len() > 0 {
				sqlB.Where().Add(&whereB).String()
			}
			lvsql := sqlB.String()
			v, e := this.DBConn.GetFirstVal(lvsql)
			if e != nil {
				err = e
				lastsql = lvsql
				return
			}

			if v == nil {
				resp.SetInt("cnt", 0)
			} else {
				resp.SetInt("cnt", v.AsInt(0))
			}
			sqlB.Reset("")
		}
	}

	posi := json.IntByName("posi", 0)
	num := json.IntByName("num", 20)
	selfun(&sqlB)
	if whereB.Len() > 0 {
		if sqlB.Whereflag == 0 {
			sqlB.Where()
		} else {
			sqlB.And()
		}
		sqlB.Add(&whereB)
	}
	if orderbyFunc != nil {
		orderbyFunc(&sqlB)
	}

	sqlB.Limit(posi, num)
	lastsql = sqlB.String()
	//utils.Log.DebugTypef("SQL", lastsql)
	cnt, err = this.ListEx(lastsql, func() *wrapper.SuperValue {
		return resp.ForceArrayByPath("list")
	}, recPreProcess)

	return
}

func (this *DAOHelper) ExecSQL(sql string) (int64, string, error) {
	err := this.PrepareDBConn()
	if err != nil {
		return -1, "", err
	}

	r, err := this.DBConn.ExecSql(sql)
	if err != nil {
		return -1, sql, err
	} else {
		return r, "", nil
	}

}

func (this *DAOHelper) ExecuteBatchSQL(sqls ...string) (int64, string, error) {
	var retErr error
	var errsql string

	var r int64 = 0

	err := this.PrepareDBConn()
	if err != nil {
		return -1, "", err
	}

	for _, v := range sqls {
		v = utils.Trim(v)
		if len(v) > 0 {
			r1, err := this.DBConn.ExecSql(v)
			if err != nil {
				retErr = err
				errsql = v
				break
			} else {
				r += r1
			}
		}
	}

	if retErr != nil {
		return -1, errsql, retErr
	}
	return r, "", nil
}

func (this *DAOHelper) FirstRecordAsJSON(qrySql string, onGetJSON func() *wrapper.SuperValue, preProcess TPreProcessRecCB) (int, error) {
	n, _, err := this.innerAction(func(db *db_utils.DBConn) (int64, string, error) {
		lst, cols, err := db.GetRowListWithColumns(qrySql)
		if err != nil {
			return 0, qrySql, err
		}

		obj := onGetJSON()
		{
			if len(lst) == 0 {
				return 0, qrySql, nil
			}

			if len(cols) < len(lst[0]) {
				return 0, qrySql, errors.New("columns is less than row")
			}

			err := JsonCopyFromRowAndColumnWithCb3(obj, lst[0], cols, preProcess)
			if err != nil {
				return 0, qrySql, err
			}
		}
		return int64(len(lst)), qrySql, err
	})

	return int(n), err
}

func (this *DAOHelper) ListEx(lvsql string, oncreatelist func() *wrapper.SuperValue, preProcess TPreProcessRecCB) (int, error) {
	n, _, err := this.innerAction(func(db *db_utils.DBConn) (int64, string, error) {
		lst, cols, err := db.GetRowListWithColumns(lvsql)
		if err != nil {
			return 0, lvsql, err
		}

		if this.MaxPackageRowCount > 0 && len(lst) > this.MaxPackageRowCount {
			return 0, lvsql, fmt.Errorf("查询数量(%d)超出最大允许打包数量(%d)", len(lst), this.MaxPackageRowCount)
		}

		rval := oncreatelist()
		if len(lst) == 0 {
			return 0, lvsql, nil
		}
		if rval.IsArray() {
			err := JsonCopyFromRowsAndColumnWithCb3Ex(rval, lst, cols, preProcess, this.QueryPackLowerFieldName)
			if err != nil {
				return 0, lvsql, err
			}
			return int64(rval.Count()), lvsql, err
		} else {
			err := JsonCopyFromRowAndColumnWithCb0(rval, lst[0], cols, preProcess, this.QueryPackLowerFieldName)
			if err != nil {
				return 0, lvsql, err
			}
			return 1, lvsql, err
		}
	})

	return int(n), err
}

func (this *DAOHelper) List(lvsql string, oncreatelist func() *wrapper.SuperValue, onerr func(err error), preProcess TPreProcessRecCB) bool {
	_, _, err := this.innerAction(func(db *db_utils.DBConn) (int64, string, error) {
		lst, cols, err := db.GetRowListWithColumns(lvsql)
		if err != nil {
			return 0, lvsql, err
		}

		rlist := oncreatelist()
		{
			err := JsonCopyFromRowsAndColumnWithCb3Ex(rlist, lst, cols, preProcess, this.QueryPackLowerFieldName)
			if err != nil {
				return 0, lvsql, err
			}
		}
		return int64(rlist.Count()), lvsql, err
	})

	if onerr != nil && err != nil {
		onerr(err)
	}
	return true
}
